# -*- coding: utf-8 -*-
"""
统一数据库操作服务（CRUD + 事务）
支持多数据库类型（SQLite / MySQL）
"""

import logging
from typing import Optional, Union, List, Any, Tuple
from contextlib import contextmanager
from PySide6.QtSql import QSqlQuery, QSqlQueryModel, QSqlRecord
from .connection_manager import ConnectionManager
from .config import DBConfig
import os
import sys

logger = logging.getLogger(__name__)


# ======================
# 🔍 查询操作 (Read)
# ======================

def get_model(
        sql: str,
        params: Optional[Union[tuple, list]] = None,
        config: Optional[DBConfig] = None
) -> QSqlQueryModel:
    """
    执行 SQL 查询并返回一个可用于 QTableView 等视图组件的只读数据模型。

    该函数封装了 Qt 的 QSqlQuery 和 QSqlQueryModel，支持参数化查询以防止 SQL 注入，
    并自动处理数据库连接。返回的模型是 **只读的**，如需支持编辑，请使用 EditableQueryModel。

    Args:
        sql (str):
            要执行的 SQL 查询语句。支持使用 ``?`` 作为位置占位符（例如：``"SELECT * FROM users WHERE id = ?"``）。
            占位符将由 ``params`` 中的值安全替换。**不能用于表名、列名等 SQL 结构部分**。

        params (Optional[Union[tuple, list]], optional):
            与 SQL 语句中 ``?`` 占位符对应的参数值列表或元组。
            - 每个 ``?`` 按顺序对应 ``params`` 中的一个元素。
            - 值可以是字符串、整数、浮点数、布尔值、None（对应 SQL 的 NULL）等。
            - 若为 ``None``（默认），则直接执行原始 SQL（不推荐用于含用户输入的场景）。
            - 示例：``params = [123, "Alice", True]``

        config (Optional[DBConfig], optional):
            数据库连接配置对象。若为 ``None``，则使用默认配置（通过内部 `_get_default_config()` 获取）。
            适用于多数据库场景（如同时连接 SQLite 和 MySQL）。

    Returns:
        QSqlQueryModel:
            已加载查询结果的数据模型，可直接设置给 ``QTableView.setModel()`` 使用。
            注意：此模型为 **只读**，调用 ``setData()`` 不会生效。

    Raises:
        RuntimeError:
            - SQL 语法错误或执行失败（如表不存在、字段错误等）。
            - 模型绑定查询失败（通常由数据库驱动错误引起）。
            错误信息包含 Qt 返回的具体错误文本，便于调试。

    Examples:
        >>> # 基本用法（无参数）
        >>> model = get_model("SELECT id, name FROM users")

        >>> # 安全参数化查询（推荐！）
        >>> user_id = 42
        >>> model = get_model(
        ...     "SELECT id, name, email FROM users WHERE active = ? AND dept_id = ?",
        ...     params=[True, user_id]
        ... )

        >>> # 在 QTableView 中使用
        >>> table_view = QTableView()
        >>> table_view.setModel(model)

    Security Note:
        **强烈建议始终使用 ``params`` 进行参数化查询**，避免将用户输入直接拼接到 ``sql`` 字符串中，
        否则可能导致严重的 SQL 注入漏洞。

    See Also:
        - :class:`EditableQueryModel`: 支持编辑、分页、排序的增强版模型。
        - :func:`execute`: 用于执行非查询类 SQL（如 INSERT/UPDATE/DELETE）。
    """
    db = ConnectionManager.get_connection(config or get_default_config())
    query = QSqlQuery(db)

    if params is not None:
        # 使用预编译语句 + 参数绑定，确保安全性和类型正确性
        query.prepare(sql)
        for p in params:
            query.addBindValue(p)
        ok = query.exec()
    else:
        # 无参数时直接执行（仅适用于完全静态、无外部输入的 SQL）
        ok = query.exec(sql)

    if not ok:
        raise RuntimeError(f"模型查询失败: {query.lastError().text()}")

    model = QSqlQueryModel()
    model.setQuery(query)
    if model.lastError().isValid():
        raise RuntimeError(f"QSqlQueryModel 设置失败: {model.lastError().text()}")

    return model


def get_record(
    sql: str,
    params: Optional[Union[tuple, list]] = None,
    config: Optional[DBConfig] = None
) -> Optional[QSqlRecord]:
    """返回第一条记录"""
    db = ConnectionManager.get_connection(config or get_default_config())
    query = QSqlQuery(db)

    if params is not None:
        query.prepare(sql)
        for p in params:
            query.addBindValue(p)
        ok = query.exec()
    else:
        ok = query.exec(sql)

    if not ok:
        raise RuntimeError(f"记录查询失败: {query.lastError().text()}")

    return query.record() if query.next() else None


def get_records(
    sql: str,
    params: Optional[Union[tuple, list]] = None,
    config: Optional[DBConfig] = None
) -> List[QSqlRecord]:
    """返回所有记录列表"""
    db = ConnectionManager.get_connection(config or get_default_config())
    query = QSqlQuery(db)

    if params is not None:
        query.prepare(sql)
        for p in params:
            query.addBindValue(p)
        ok = query.exec()
    else:
        ok = query.exec(sql)

    if not ok:
        raise RuntimeError(f"记录列表查询失败: {query.lastError().text()}")

    records = []
    while query.next():
        records.append(query.record())
    return records


def fetch_one(
    sql: str,
    params: Optional[Union[tuple, list]] = None,
    config: Optional[DBConfig] = None
) -> Optional[List[Any]]:
    """返回第一条记录的值列表（不带字段名）"""
    rec = get_record(sql, params, config)
    return [rec.value(i) for i in range(rec.count())] if rec else None


def fetch_all(
    sql: str,
    params: Optional[Union[tuple, list]] = None,
    config: Optional[DBConfig] = None
) -> List[List[Any]]:
    """返回所有记录的值列表（二维列表）"""
    records = get_records(sql, params, config)
    return [[rec.value(i) for i in range(rec.count())] for rec in records]


# ======================
# ✏️ 写入操作 (Create / Update / Delete)
# ======================

def execute(
    sql: str,
    params: Optional[Union[tuple, list]] = None,
    config: Optional[DBConfig] = None
) -> int:
    """
    执行任意写入 SQL（INSERT/UPDATE/DELETE），返回受影响行数
    """
    db = ConnectionManager.get_connection(config or get_default_config())
    query = QSqlQuery(db)

    if params is not None:
        query.prepare(sql)
        for p in params:
            query.addBindValue(p)
        ok = query.exec()
    else:
        ok = query.exec(sql)

    if not ok:
        raise RuntimeError(f"SQL 执行失败: {query.lastError().text()}")

    return query.numRowsAffected()


def insert(
    table: str,
    data: dict,
    config: Optional[DBConfig] = None
) -> int:
    """插入一条记录"""
    if not data:
        raise ValueError("插入数据不能为空")

    fields = list(data.keys())
    placeholders = ", ".join(["?"] * len(fields))
    sql = f"INSERT INTO {table} ({', '.join(fields)}) VALUES ({placeholders})"
    params = list(data.values())

    return execute(sql, params, config)


def update(
    table: str,
    data: dict,
    where: str,
    where_params: Optional[Union[list, tuple]] = None,
    config: Optional[DBConfig] = None
) -> int:
    """更新记录"""
    if not data:
        raise ValueError("更新数据不能为空")

    set_clause = ", ".join([f"{field} = ?" for field in data.keys()])
    params = list(data.values())
    if where_params:
        params.extend(where_params)

    sql = f"UPDATE {table} SET {set_clause} WHERE {where}"
    return execute(sql, params, config)


def delete(
    table: str,
    where: str,
    where_params: Optional[Union[list, tuple]] = None,
    config: Optional[DBConfig] = None
) -> int:
    """删除记录"""
    sql = f"DELETE FROM {table} WHERE {where}"
    return execute(sql, where_params, config)


# ======================
# 🔄 事务支持
# ======================

@contextmanager
def transaction(config: Optional[DBConfig] = None):
    """
    事务上下文管理器
    注意：事务内所有操作必须使用相同的 config！
    """
    db = ConnectionManager.get_connection(config or get_default_config())
    if not db.transaction():
        raise RuntimeError("无法开启事务")

    try:
        yield
        if not db.commit():
            raise RuntimeError("事务提交失败")
    except Exception:
        db.rollback()
        raise

def execute_sql_list_in_transaction(sql_list: List[str], config: Optional[DBConfig] = None) -> Tuple[bool, str]:
    """用原有 transaction 上下文管理器执行 SQL 列表事务"""
    try:
        # 开启事务（包裹所有 SQL 执行逻辑）
        with transaction(config=config):
            for idx, sql in enumerate(sql_list, start=1):
                # 循环执行每条 SQL（调用你现有 execute 函数）
                execute(sql, config=config)  # 无参数 SQL，params 传 None
        return True, "所有 SQL 事务执行成功"
    except Exception as e:
        # 任意一条失败，自动回滚，返回错误信息
        return False, f"事务执行失败：第 {idx} 条 SQL 出错\n错误原因：{str(e)}\n出错 SQL：{sql}"



# ======================
# 🛠 默认配置（开发友好）
# ======================

_DEFAULT_CONFIG = None

import sys
import os
from .config import DBConfig

_DEFAULT_CONFIG = None


def get_default_config() -> DBConfig:
    """懒加载默认配置（SQLite），适配打包后 _internal/data 目录"""
    global _DEFAULT_CONFIG
    if _DEFAULT_CONFIG is None:
        if hasattr(sys, '_MEIPASS'):
            # 打包后：执行程序所在目录 / _internal / data / 数据库文件
            # 关键：用 sys.executable 获取执行程序路径，再拼接 _internal
            exe_path = sys.executable  # 例如：D:\dist\your_app.exe
            exe_dir = os.path.dirname(exe_path)  # 执行程序所在目录：D:\dist
            # 拼接目标路径：D:\dist\_internal\data\middleDatabase.db
            default_path = os.path.join(
                exe_dir, "_internal", "data", "middleDatabase.db"
            )
        else:
            # 开发环境：项目根目录/data/...
            default_path = os.path.join(os.getcwd(), "data", "middleDatabase.db")

        # 强制创建目录（即使打包后也确保存在）
        data_dir = os.path.dirname(default_path)
        os.makedirs(data_dir, exist_ok=True)

        # 验证路径可写（关键！避免权限问题）
        if not os.access(data_dir, os.W_OK):
            raise RuntimeError(f"_internal/data 目录无写入权限：{data_dir}")

        _DEFAULT_CONFIG = DBConfig(type="sqlite", path=default_path)

    return _DEFAULT_CONFIG